/*
 * Scheduler used for scheduling processes.
 * 
 * Also handles the lines being read from the file, and
 * the data.  Move this to BRAIN10.c?
 * 
 * scheduler.c
 */

#include <stdio.h>
#include <string.h>
#include <sys/time.h>
#include "mailman.h"
#include "semaphore.h"
#include "BRAIN10.h"
#include "scheduler.h"
#include "instructions.h"

/*
 * Method for storing brain10 instructions and number of processes.
 */
void handleLine(int lineNum, char line[50], int numChars, BRAIN10 *brain10, VBRAIN10 *vbrain10)
{
    /*Checking for # of page tables needed by processor. 
     This also increments total num of processes.
     */
    if (numChars == 1 || numChars == 2)
    {
        char processPages[2];
        processPages[0] = line[0];
        processPages[1] = line[1];
        
        int pageframes = atoi(processPages);
        vbrain10 -> BR[brain10 -> numProcesses] = brain10 -> BR;
        brain10 -> BR += pageframes * 10;
        vbrain10 -> LR[brain10 -> numProcesses] = (brain10 -> BR - 1);
    }
    
    /* Checking for BRAIN10s in the Brain file because that
     * means a new process.
     */
    else if (numChars == 7)
    {
        int numP = brain10 -> numProcesses;
        //printf("Storing process %d instructions: \n", numP);
        brain10 -> numProcesses++;
    }
    
    // Checking for commands, they should be 4 characters long
    else 
        allocateMemory(line, brain10, vbrain10);
}

/*
 * Method for storing the BRAIN10 data.
 * Starts at memory location 9000.
 */
void handleData(int dataLine, char line[50], BRAIN10 *brain10)
{	
	int i, j, lineIndx;
	lineIndx = 0;
        // Looping the 10 sections of data
	for (i = 0; i < 10; i++)
	{
            // Loop the 4 characters
            for (j = 0; j < 4; j++)	
            {
                // Storing algorithm, starts at mem location 9000
                brain10 -> storage[9000+(dataLine * 10)+i][j] = line[lineIndx];
                //printf("line indx: %d, scheduler -> storage[%d][%d] = %c\n", lineIndx, (9000 + (dataLine * 10) + i), j, line[lineIndx]);
                lineIndx++;
            }
            // Skip the space between the 4 character words of data
            lineIndx++;
	}
}

/*
 * Method for printing memory.
 */
void printMem(VBRAIN10 *vbrain10)
{
    int i, j, k;
    for (i = 0; i < 10; i++)
    {
        for (j = 0; j < 100; j++)
        {
            for (k = 0; k < 4; k++)
                printf("mem[%d][%d][%d] = %c\n", i, j, k, vbrain10 -> vStorage[i][j][k]);
        }
    }
}

/*
 * Scheduling algorithm.
 * timeSlice = time quantum, default being 2
 */
void beginScheduling(BRAIN10 *brain10, VBRAIN10 *vbrain10, MAILMAN *mailman, SEMAPHORES *semaphores)
{
    // start and end used for timing scheduling
    struct timeval start,end;
    
    int timeSlice = 5;
    double schdTime = 0;
    int i, j, numP;
    numP = brain10 -> numProcesses;
    int instArr[numP], maxInst[numP];
    
    // Checking how many instructions each process has
    for (i = 0; i < numP; i++)
    {
        int sum;
        sum = 0;
        maxInst[i] = vbrain10 -> vIC[i];
        vbrain10 -> vIC[i] = 0;
        for (j = 0; j < vbrain10 -> LR[i]; j++)
        {
            // If we reach a #, we know there are no more instructions for this process
            if (brain10 -> storage[8000+(vbrain10 -> BR[i])+j][0] == '#')
                break;
            // else increment the instruction count
            else
                sum++;
        }
        instArr[i] = sum;
        printf("P%d: %d instructions to execute.\n", i, sum);
        pushReadyProcess(brain10, i);
    }
    
    // While there are processes to execute.
    while (!isEmpty(brain10))
    {
        int process = popReadyProcess(brain10);
        
        // Get the start of the scheduling
        gettimeofday(&start, NULL);

        int tempIC;
        // Don't execute a processes instructions if there are none
        if (vbrain10 -> vIC[process] >= maxInst[process])
        {
            continue;
        } else if (vbrain10 -> BR[process] + vbrain10 -> vIC[process] > vbrain10 -> LR[process]) {
            printf("SEGMENTATION FAULT I DON'T KNOW WHERE THE FUCK YOUR TRYING TO INDEX!");
        } else {
            // Execute the time quantum of instructions
            int blocked, waiting;
            blocked = 0;
            waiting = 0;
            for (j = 0; j < timeSlice; j++)
            {
                int iCounter = vbrain10 -> vIC[process];
                char nextInstruction[4];
                int k;
                for (k = 0; k < 4; k++) {
                    nextInstruction[k] = brain10 -> storage[8000+vbrain10 -> BR[process] + iCounter][k];
                }

                char operand[2];
                operand[0] = nextInstruction[2];
                operand[1] = nextInstruction[3];
                if(nextInstruction[0] == 'R' && nextInstruction[1] =='C') {
                    if(isBlocked(mailman, process, operand)){
                        blocked = 1; 
                        break; // Block
                    } else {
                        waiting = 0;
                        blocked = 0;
                        goto exeStatement; // Execute
                    }                        
                // Else continue with the normal executing
                } else {
                    // Check if process is waiting first
                    if(isWaiting(semaphores, atoi(operand), process)) {
                        waiting = 1;
                        break; // Block - Wait
                    } else {
                        waiting = 0;
                        blocked = 0;
                        goto exeStatement; // Execute
                    }
                }

                /*
                 * Execute statement for goto.  Used instead of
                 * creating a new method, much cleaner and easier.
                 */
                exeStatement:
                    printf("P%d executing instruction: %d\n", process, iCounter);
                    executeInstruction(process, nextInstruction, brain10, vbrain10, mailman,semaphores);
                    if(vbrain10 -> vIC[process] == iCounter) // Conditional incase of a jump instruction.
                    {
                        vbrain10 -> vIC[process]++;
                        if(iCounter >= tempIC) // Store the last instructions if and only if the last instruction was not an instruction already executed previously.
                        {
                            instArr[process]--;
                            tempIC++;
                        }
                    }
                    else //This handles the case of the initial jump.
                    {
                        if(iCounter >= tempIC)
                        {
                            instArr[process]--;
                            tempIC++;
                        }
                    }
            }
            
            if(!waiting && !blocked){
                pushReadyProcess(brain10, process);
            }
        }
    }
    printf("----------------------------\n");
    // Get end time of the time quantum, add it to the total time
    gettimeofday(&end, NULL);
    schdTime = schdTime + (end.tv_usec - start.tv_usec);
    printf("Total runtime for scheduling and execution: %f\n", schdTime);
}